package cn.jnetty.utils.create.proxy;

import cn.jnetty.api.annotation.*;
import cn.jnetty.utils.create.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 猪哥亮
 * @email 1030681978@qq.com
 * @date 2020/4/20 14:26
 * 描述：
 */
public class JnettyProxyCreateBuild {
    private static final String PACKAGE_INFO_CLASS = "package-info.class";
    private static final String CLASS_SUFFIX = ".class";

    public static void main(String[] args) {
        iocLoader(null);
    }


    public static void iocLoader(CreateEntity buildPath) {
        JnettyResourceScanning.scanPackage(buildPath.getScanPackages(), (resource) -> analysisResource(resource, buildPath));
    }

    private static void analysisResource(Resource resource, CreateEntity buildPath) {
        String resourceName = resource.getName();

        // 忽略 package-info.class
        if (PACKAGE_INFO_CLASS.equals(resourceName)) {
            return;
        }

        // 忽略非Class文件
        if (!resourceName.endsWith(CLASS_SUFFIX)) {
            return;
        }
        Class<?> clazz = ClassUtils.loadClass(resourceName.substring(0, resourceName.length() - 6).replaceAll("[/\\\\]", "."));
        Annotation proco = clazz.getAnnotation(JRequestMapping.class);
        if (proco instanceof JRequestMapping) {
            JRequestMapping jp = JRequestMapping.class.cast(proco);
            getObjectInfo(clazz, jp, buildPath);
        }

    }

    private static void getObjectInfo(Class<?> clazz, JRequestMapping jpm, CreateEntity buildPath) {
        Method[] methods = clazz.getDeclaredMethods();
        List<CsMethodModel> methodModels = new ArrayList<>();
        for (Method m : methods) {

            //获取方法注释
            Annotation[] annotations = m.getDeclaredAnnotations();
            String desc = "";
            byte cmd = 0;
            boolean isOk = false;
            for (Annotation annotation : annotations) {
                if (annotation.annotationType() == JTcpMapping.class) {
                    JTcpMapping jt = JTcpMapping.class.cast(annotation);
                    desc = jt.explain();
                    cmd = Byte.parseByte(jt.path());
                    isOk = true;
                } else if (annotation.annotationType() == JUdpMapping.class) {
                    JUdpMapping ju = JUdpMapping.class.cast(annotation);
                    desc = ju.explain();
                    cmd = Byte.parseByte(ju.path());
                    isOk = true;
                } else if (annotation.annotationType() == JWebsocketMapping.class) {
                    JWebsocketMapping jw = JWebsocketMapping.class.cast(annotation);
                    desc = jw.explain();
                    cmd = Byte.parseByte(jw.path());
                    isOk = true;
                } else if (annotation.annotationType() == JPostMapping.class) {
                    JPostMapping jp = JPostMapping.class.cast(annotation);
                    desc = jp.explain();
                    cmd = Byte.parseByte(jp.path());
                    isOk = true;
                } else if (annotation.annotationType() == JGetMapping.class) {
                    JGetMapping jg = JGetMapping.class.cast(annotation);
                    desc = jg.explain();
                    cmd = Byte.parseByte(jg.path());
                    isOk = true;
                }
            }
            if (!isOk) {
                continue;
            }
            CsMethodModel csMethodModel = new CsMethodModel();
            csMethodModel.setMethodExplain(desc);
            csMethodModel.setCmd(cmd);
            csMethodModel.setMethodName(m.getName());
            csMethodModel.setReturnType(m.getGenericReturnType().getTypeName());
            Parameter[] parameters = m.getParameters();
            ParamertModel[] paramertModels = new ParamertModel[parameters.length];
            for (int i = 0; i < paramertModels.length; i++) {
                Parameter p = parameters[i];
                String str = p.getParameterizedType().getTypeName();
                if (clazz.isInterface()) {
                    paramertModels[i] = new ParamertModel(str, p.getName());
                } else {
                    String str1 = StringUtils.substringAfterLast(str, ".");
                    str1 = str1 == null || str1.equals("") ? str : str1;
                    paramertModels[i] = new ParamertModel(str1, p.getName());
                }
            }
            csMethodModel.setMethodTypes(paramertModels);
            methodModels.add(csMethodModel);
        }
        CsModel csModel = new CsModel();
        Annotation proxy = clazz.getAnnotation(JnettyClassExplain.class);
        String desc = "";
        if (proxy instanceof JnettyClassExplain) {
            JnettyClassExplain classDesc = (JnettyClassExplain) proxy;
            //获取接口注释
            desc = classDesc.value();
        }
        csModel.setClassExplain(desc);
        csModel.setInterfaceName(clazz);
        csModel.setImplementsInterfaceName("");
        csModel.setMethodModels(methodModels);
        if (clazz.isInterface()) {
            notifyProxyCs(csModel, jpm, buildPath.getCsProxyPath());
            notifyProxyJava(csModel, jpm, buildPath.getJvProxyPath());
        } else {
            syncSpliceCsProxy(csModel, jpm, buildPath.getCsProxyPath());
        }
    }

    /**
     * 生成同步回调的客户端请求协议类
     *
     * @param csModels
     */
    public static void syncSpliceCsProxy(CsModel csModels, JRequestMapping jp, String createPath) {
        StringBuilder sb = new StringBuilder();
        String desc = classDesc(csModels.getClassExplain());
        if (desc != null) {
            sb.append(desc);
        }
        String s = csModels.getInterfaceName().getName();
        sb.append("namespace " + StringUtils.substringBeforeLast(s, ".") + "{\n");
        sb.append("\tusing System;\n");
        sb.append("\tusing System.Threading.Tasks;\n");
        sb.append("\tusing System.Collections.Generic;\n");
        sb.append("\tusing cn.jnetty.utils.io;\n");
        s = StringUtils.substringAfterLast(s, ".");
        String id = jp.value();
        sb.append("\tpublic class " + s + " { \n");
        for (CsMethodModel cmd : csModels.getMethodModels()) {
            String returnType = TypeUtils.getArrType(cmd.getReturnType(), cmd.getMethodExplain(), cmd.getMethodName());
            String sss = methodDesc(cmd.getMethodExplain());
            if (sss != null) {
                sb.append(sss);
            }
            if (!TypeUtils.isPrimitive(cmd.getReturnType())) {
                //IoPojo$BottomPourVo$Builder
                returnType = StringUtils.substringBeforeLast(returnType,"$");
                returnType = StringUtils.replace(returnType,"$",".");
            }
            sb.append("\t\tpublic Task<" + returnType + "> " + cmd.getMethodName() + "(");
            String s1 = "";
            String[] argsName = new String[cmd.getMethodTypes().length];
            String[] types = new String[cmd.getMethodTypes().length];
            for (int i = 0; i < cmd.getMethodTypes().length; i++) {
                String str = cmd.getMethodTypes()[i].getType();
                if (!TypeUtils.isPrimitive(str)) {
                    //str = StringUtils.substringBeforeLast(str,"$");
                    str = StringUtils.replace(str,"$",".");
                } else if (str.equals("String")) {
                    str = "string";
                } else if (str.equals("boolean")) {
                    str = "bool";
                }
                s1 += str + " " + cmd.getMethodTypes()[i].getName() + ",";
                argsName[i] = cmd.getMethodTypes()[i].getName();
                types[i] = str;
            }
            s1 = s1.equals("") ? "" : StringUtils.substringBeforeLast(s1, ",");
            sb.append(s1);
            sb.append("){\n");
            //拼参数
            byte methodCmd = cmd.getCmd();
            if (argsName.length > 1) {
                throw new RuntimeException("id为<<" + id + ">>的<" + cmd.getMethodName() + ">的参数大于一个");
            }
            if (argsName.length == 0) {
                sb.append("\t\t\treturn JNettySocket.Instance.Call<")
                        .append(returnType)
                        .append(">(")
                        .append(id)
                        .append(",")
                        .append(methodCmd)
                        .append(",null);\n")
                        .append("\t\t}\n");
            } else {
                String an = argsName[0];
                sb.append("\t\t\tbyte[] buff = Bytes.Obj2bytes(" + an + ");\n");
                sb.append("\t\t\treturn JNettySocket.Instance.Call<")
                        .append(returnType)
                        .append(">(")
                        .append(id)
                        .append(",")
                        .append(methodCmd)
                        .append(", buff);\n")
                        .append("\t\t}\n");
            }
        }
        sb.append("\t}\n");
        sb.append("}");
        JnettyCreate.create(csModels.getInterfaceName().getSimpleName() + ".cs", sb.toString(), createPath);
    }

    private static void notifyProxyJava(CsModel csModels, JRequestMapping jp, String createPath) {
        StringBuilder clazz = new StringBuilder();
        StringBuilder importInfo = new StringBuilder();
        String s = csModels.getInterfaceName().getName();
        s = StringUtils.substringAfterLast(s, ".");
        String packageName = csModels.getInterfaceName().getName();
        int index = packageName.lastIndexOf(".");
        importInfo.append("package ")
                .append(packageName.substring(0, index))
                .append(";\n")
                .append("import cn.jnetty.api.annotation.JComponent;\n")
                .append("import ")
                .append(csModels.getInterfaceName().getName())
                .append(";\n")
                .append("import cn.jnetty.remoting.code.JnettyTcpWrapperIoMessage;\n")
                .append("import cn.jnetty.utils.create.dto.PrimitiveDto;\n")
                .append("import cn.gf.nn.pojo.proto.BytesArray;\n")
                .append("import cn.gf.nn.pojo.proto.*;\n")
                .append("import cn.jnetty.utils.create.dto.PrimitiveManager;\n")
                .append("import cn.jnetty.utils.JnettyBufferSerializableUtil;\n");
        clazz.append("/**\n")
                .append(" * @author 猪哥亮\n")
                .append(" * @email 1030681978@qq.com\n")
                .append(" *描述： ")
                .append(csModels.getClassExplain())
                .append("\n */\n")
                .append("@JComponent\n")
                .append("public class ")
                .append(s)
                .append("Impl ")
                .append("implements ")
                .append(s)
                .append("{\n");
        boolean isImport = false;
        for (CsMethodModel cmd : csModels.getMethodModels()) {
            String sss = javaMethodDesc(cmd.getMethodExplain());
            if (sss != null) {
                clazz.append(sss);
            }
            clazz.append("\t@Override\n");
            clazz.append("\tpublic ")
                    .append(StringUtils.substringAfterLast(cmd.getReturnType(), "."))
                    .append(" ")
                    .append(cmd.getMethodName())
                    .append("(");
            String s1 = "";
            String[] argsName = new String[cmd.getMethodTypes().length];
            StringBuilder sb1 = new StringBuilder();
            String nf = "nf";
            for (int i = 0; i < cmd.getMethodTypes().length; i++) {
                String str = TypeUtils.getArrType(cmd.getMethodTypes()[i].getType(), s, cmd.getMethodName());
                if (!TypeUtils.isPrimitive(cmd.getMethodTypes()[i].getType())) {
                    str = StringUtils.replace(str, "$", ".");
                } else {
                    str = cmd.getMethodTypes()[i].getType();
                    sb1.append("\t\tPrimitiveDto dto = PrimitiveManager.get(")
                            .append(cmd.getMethodTypes()[i].getType())
                            .append(".class);\n");
                    sb1.append("\t\tdto.setValue(nf);\n");
                    nf = "dto.builder()";
                }

                s1 += str + " " + cmd.getMethodTypes()[i].getName() + ",";

                argsName[i] = cmd.getMethodTypes()[i].getName();
            }
            s1 = s1.equals("") ? "" : StringUtils.substringBeforeLast(s1, ",");
            clazz.append(s1);
            clazz.append("){\n");
            clazz.append(sb1);
            //return new JnettyTcpWrapperIoMessage((short)111,(byte)1,(byte)18,BufferSerializableUtil.toByteArray(nf));
            clazz.append("\t\treturn new ")
                    .append(StringUtils.substringAfterLast(cmd.getReturnType(), "."))
                    .append("((short)")
                    .append(Integer.valueOf(jp.value()))
                    .append(",")
                    .append("(byte)")
                    .append(cmd.getCmd())
                    .append(",(byte)")
                    .append(18)
                    .append(",");
            if (cmd.getMethodTypes().length == 0) {
                clazz.append("null);\n");
            } else {
                clazz.append("JnettyBufferSerializableUtil.toByteArray(")
                        .append(nf)
                        .append("));\n");
            }
            clazz.append("\t}\n");

        }
        clazz.append("}\n");
        importInfo.append(clazz.toString());
        JnettyCreate.create(csModels.getInterfaceName().getSimpleName() + "Impl.java", importInfo.toString(), createPath + "//broadcasts//");
    }

    /**
     * 拼接cs对象
     *
     * @param csModels
     */
    private static void notifyProxyCs(CsModel csModels, JRequestMapping jp, String createPath) {
        StringBuilder sb = new StringBuilder();
        String desc = classDesc(csModels.getClassExplain());
        if (desc != null) {
            sb.append(desc);
        }
        String s = csModels.getInterfaceName().getName();
        sb.append("namespace " + StringUtils.substringBeforeLast(s, ".") + "{\n");
        sb.append("\tusing System;\n");
        sb.append("\tusing System.Collections.Generic;\n");
        s = StringUtils.substringAfterLast(s, ".");
        sb.append("\t[Proxy(" + Integer.parseInt(jp.value()) + ")]\n");
        sb.append("\tpublic class " + s + " { \n");
        for (CsMethodModel cmd : csModels.getMethodModels()) {
            String sss = methodDesc(cmd.getMethodExplain());
            if (sss != null) {
                sb.append(sss);
            }
            sb.append("\t\t[MethodProxy(");
            sb.append(cmd.getCmd());
            sb.append(")]\n");
            sb.append("\t\tpublic virtual void " + cmd.getMethodName() + "(");
            String s1 = "";
//            String[] argsName = new String[cmd.getMethodTypes().length];
            for (int i = 0; i < cmd.getMethodTypes().length; i++) {
                if (TypeUtils.isPrimitive(cmd.getMethodTypes()[i].getType())) {
                    if (cmd.getMethodTypes()[i].getType().equals(void.class.getName())) {
                        continue;
                    }
                    String str = TypeUtils.getArrType(cmd.getMethodTypes()[i].getType(), s, cmd.getMethodName());
                    s1 += str + " " + cmd.getMethodTypes()[i].getName() + ",";
                } else {
                    String str = StringUtils.substringAfterLast(cmd.getMethodTypes()[i].getType(), ".");
                    str = StringUtils.substringBeforeLast(str,"$");
                    s1 = StringUtils.replace(str,"$",".")+ " " + cmd.getMethodTypes()[i].getName() + ",";
//                    if (str.contains("KvDto") || str.contains("BytesBuffor")) {
//                        s1 = str + " " + cmd.getMethodTypes()[i].getName() + ",";
//                    } else {
//                        s1 = str + " " + cmd.getMethodTypes()[i].getName() + ",";
//                    }
                }
            }
            s1 = s1 == null || s1.equals("") ? "" : StringUtils.substringBeforeLast(s1, ",");
            sb.append(s1);
            sb.append("){}\n");
        }
        sb.append("\t}\n");
        sb.append("}");
        JnettyCreate.create(csModels.getInterfaceName().getSimpleName() + ".cs", sb.toString(), createPath + "//broadcasts//");
    }

    private static String classDesc(String s) {
        if (s == null && s.equals("")) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        sb.append("/// <summary>\n");
        sb.append("/// " + s + "\n");
        sb.append("/// </summary>\n");
        return sb.toString();
    }

    private static String methodDesc(String s) {
        if (s == null && s.equals("")) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        sb.append("\t\t/// <summary>\n");
        sb.append("\t\t/// " + s + "\n");
        sb.append("\t\t/// </summary>\n");
        return sb.toString();
    }

    private static String javaMethodDesc(String s) {
        if (s == null && s.equals("")) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        sb.append("\t/**\n");
        sb.append("\t * " + s + "\n");
        sb.append("\t */\n");
        return sb.toString();
    }
}
